// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include <string>
#include "dllmain.h"
#include <TlHelp32.h>
#include <Shlwapi.h>
#include "atlconv.h"
#include <tchar.h>

#pragma comment(lib, "shlwapi")


#define WECHAT_PROCESS_NAME "WeChat.exe"
using namespace std;

int PatchWeChat();

BOOL ElevatePrivileges();
int GetProcIds(DWORD* Pids);
BOOL IsTargetPid(DWORD Pid, DWORD* Pids, int num);
HANDLE DuplicateHandleEx(DWORD pid, HANDLE h, DWORD flags);
int OpenWeChat(char* dllPath, int isCreateGolb);
BOOL CheckIsInject(DWORD dwProcessid);
DWORD ProcessNameFindPID(const char* ProcessName);


#pragma region 打开微信并注入


BOOL CheckIsInject(DWORD dwProcessid)
{
	HANDLE hModuleSnap = INVALID_HANDLE_VALUE;
	//初始化模块信息结构体
	MODULEENTRY32 me32 = { sizeof(MODULEENTRY32) };
	//创建模块快照 1 快照类型 2 进程ID
	hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessid);
	//如果句柄无效就返回false
	if (hModuleSnap == INVALID_HANDLE_VALUE)
	{
		MessageBoxA(NULL, "创建模块快照失败", "错误", MB_OK);
		return FALSE;
	}
	//通过模块快照句柄获取第一个模块的信息
	if (!Module32First(hModuleSnap, &me32))
	{
		MessageBoxA(NULL, "获取第一个模块的信息失败", "错误", MB_OK);
		//获取失败则关闭句柄
		CloseHandle(hModuleSnap);
		return FALSE;
	}
	do
	{
		if (StrCmpW(me32.szModule,L"wechathelper.dll") == 0)
		{
			return FALSE;
		}

	} while (Module32Next(hModuleSnap, &me32));
	return TRUE;
}



int OpenWeChat(char* dllPath, int isCreateGolb)
{
	int pid = 0;
	//HKEY_CURRENT_USER\Software\Tencent\WeChat InstallPath = xx
	HKEY hKey = NULL;
	if (ERROR_SUCCESS != RegOpenKey(HKEY_CURRENT_USER, L"Software\\Tencent\\WeChat", &hKey))
	{
		return pid;
	}

	DWORD Type = REG_SZ;
	WCHAR Path[MAX_PATH] = { 0 };
	DWORD cbData = MAX_PATH * sizeof(WCHAR);
	if (ERROR_SUCCESS != RegQueryValueEx(hKey, L"InstallPath", 0, &Type, (LPBYTE)Path, &cbData))
	{
		goto __exit;
	}

	PathAppend(Path, L"WeChat.exe");

	//ShellExecute(NULL, L"Open", Path, NULL, NULL, SW_SHOW);

__exit:
	if (hKey)
	{
		RegCloseKey(hKey);
	}

	STARTUPINFO si;
	PROCESS_INFORMATION pi;
	ZeroMemory(&si, sizeof(si));
	ZeroMemory(&pi, sizeof(pi));
	CreateProcess(Path, NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
	HWND  hWechatMainForm = NULL;
	//WeChatLoginWndForPC
	while (NULL == hWechatMainForm)
	{
		hWechatMainForm = FindWindow(TEXT("WeChatLoginWndForPC"), NULL);
		Sleep(500);
	}
	if (NULL == hWechatMainForm)
	{
		return FALSE;
	}
	DWORD dwPid = pi.dwProcessId;
	HANDLE wxPid = pi.hProcess;
	pid = dwPid;
	//检测dll是否已经注入
	if (CheckIsInject(dwPid))
	{
		//打开进程
		HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
		if (hProcess == NULL)
		{
			MessageBoxA(NULL, "进程打开失败", "错误", 0);
			return FALSE;
		}
		//在微信进程中申请内存
		LPVOID pAddress = VirtualAllocEx(hProcess, NULL, MAX_PATH, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
		if (pAddress == NULL)
		{
			MessageBoxA(NULL, "内存分配失败", "错误", 0);
			return FALSE;
		}
		//写入dll路径到微信进程
		if (WriteProcessMemory(hProcess, pAddress, dllPath, MAX_PATH, NULL) == 0)
		{
			MessageBoxA(NULL, "路径写入失败", "错误", 0);
			return FALSE;
		}

		//获取LoadLibraryA函数地址
		FARPROC pLoadLibraryAddress = GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryA");
		if (pLoadLibraryAddress == NULL)
		{
			MessageBoxA(NULL, "获取LoadLibraryA函数地址失败", "错误", 0);
			return FALSE;
		}
		//远程线程注入dll
		HANDLE hRemoteThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pLoadLibraryAddress, pAddress, 0, NULL);
		if (hRemoteThread == NULL)
		{
			MessageBoxA(NULL, "远程线程注入失败", "错误", 0);
			return FALSE;
		}

		CloseHandle(hRemoteThread);
		CloseHandle(hProcess);
	}
	else
	{
		MessageBoxA(NULL, "dll已经注入，请勿重复注入", "提示", 0);
		return FALSE;
	}
	return pid;
}



#pragma endregion


#pragma region hook互斥体




int PatchWeChat()
{
	DWORD dwSize = 0;
	POBJECT_NAME_INFORMATION pNameInfo;
	POBJECT_NAME_INFORMATION pNameType;
	PVOID pbuffer = NULL;
	NTSTATUS Status;
	int nIndex = 0;
	DWORD dwFlags = 0;
	char szType[128] = { 0 };
	char szName[512] = { 0 };

	ElevatePrivileges();

	DWORD Pids[100] = { 0 };

	DWORD Num = GetProcIds(Pids);
	if (Num == 0)
	{
		return 0;
	}

	if (!ZwQuerySystemInformation)
	{
		goto Exit0;
	}

	pbuffer = VirtualAlloc(NULL, 0x1000, MEM_COMMIT, PAGE_READWRITE);

	if (!pbuffer)
	{
		goto Exit0;
	}

	Status = ZwQuerySystemInformation(SystemHandleInformation, pbuffer, 0x1000, &dwSize);

	if (!NT_SUCCESS(Status))
	{
		if (STATUS_INFO_LENGTH_MISMATCH != Status)
		{
			goto Exit0;
		}
		else
		{
			// 这里大家可以保证程序的正确性使用循环分配稍好
			if (NULL != pbuffer)
			{
				VirtualFree(pbuffer, 0, MEM_RELEASE);
			}

			if (dwSize * 2 > 0x4000000)  // MAXSIZE
			{
				goto Exit0;
			}

			pbuffer = VirtualAlloc(NULL, dwSize * 2, MEM_COMMIT, PAGE_READWRITE);

			if (!pbuffer)
			{
				goto Exit0;
			}

			Status = ZwQuerySystemInformation(SystemHandleInformation, pbuffer, dwSize * 2, NULL);

			if (!NT_SUCCESS(Status))
			{
				goto Exit0;
			}
		}
	}

	{
		PSYSTEM_HANDLE_INFORMATION1 pHandleInfo = (PSYSTEM_HANDLE_INFORMATION1)pbuffer;

		for (nIndex = 0; nIndex < pHandleInfo->NumberOfHandles; nIndex++)
		{
			if (IsTargetPid(pHandleInfo->Handles[nIndex].UniqueProcessId, Pids, Num))
			{
				//
				HANDLE hHandle = DuplicateHandleEx(pHandleInfo->Handles[nIndex].UniqueProcessId,
					(HANDLE)pHandleInfo->Handles[nIndex].HandleValue,
					DUPLICATE_SAME_ACCESS
				);
				if (hHandle == NULL) continue;

				Status = NtQueryObject(hHandle, ObjectNameInformation, szName, 512, &dwFlags);

				if (!NT_SUCCESS(Status))
				{
					CloseHandle(hHandle);
					continue;
				}

				Status = NtQueryObject(hHandle, ObjectTypeInformation, szType, 128, &dwFlags);

				if (!NT_SUCCESS(Status))
				{
					CloseHandle(hHandle);
					continue;
				}

				pNameInfo = (POBJECT_NAME_INFORMATION)szName;
				pNameType = (POBJECT_NAME_INFORMATION)szType;

				WCHAR TypName[1024] = { 0 };
				WCHAR Name[1024] = { 0 };

				wcsncpy_s(TypName, (WCHAR*)pNameType->Name.Buffer, pNameType->Name.Length / 2);
				wcsncpy_s(Name, (WCHAR*)pNameInfo->Name.Buffer, pNameInfo->Name.Length / 2);

				// 匹配是否为需要关闭的句柄名称
				if (0 == wcscmp(TypName, L"Mutant"))
				{
					//WeChat_aj5r8jpxt_Instance_Identity_Mutex_Name
					//if (wcsstr(Name, L"_WeChat_App_Instance_Identity_Mutex_Name"))
					if (wcsstr(Name, L"_WeChat_") &&
						wcsstr(Name, L"_Instance_Identity_Mutex_Name"))
					{
						CloseHandle(hHandle);

						hHandle = DuplicateHandleEx(pHandleInfo->Handles[nIndex].UniqueProcessId,
							(HANDLE)pHandleInfo->Handles[nIndex].HandleValue,
							DUPLICATE_CLOSE_SOURCE
						);

						if (hHandle)
						{
							//printf("+ Patch wechat success!\n");
							CloseHandle(hHandle);
						}
						else
						{
							//printf("- Patch error: %d\n", GetLastError());
						}

						goto Exit0;
					}
				}

				CloseHandle(hHandle);
			}

		}
	}
Exit0:
	if (NULL != pbuffer)
	{
		VirtualFree(pbuffer, 0, MEM_RELEASE);
	}

	return 0;
}


//进程提权
BOOL ElevatePrivileges()
{
	HANDLE hToken;
	TOKEN_PRIVILEGES tkp;
	tkp.PrivilegeCount = 1;
	if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
		return FALSE;
	LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tkp.Privileges[0].Luid);
	tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
	if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(TOKEN_PRIVILEGES), NULL, NULL))
	{
		return FALSE;
	}

	return TRUE;
}


int GetProcIds(DWORD* Pids)
{
	PROCESSENTRY32 pe32 = { sizeof(pe32) };
	int num = 0;

	HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (hSnap)
	{
		if (Process32First(hSnap, &pe32))
		{
			do {
				if (!_wcsicmp(L"WeChat.exe", pe32.szExeFile))
				{
					if (Pids)
					{
						Pids[num++] = pe32.th32ProcessID;
					}
				}
			} while (Process32Next(hSnap, &pe32));
		}
		CloseHandle(hSnap);
	}

	return num;
}

BOOL IsTargetPid(DWORD Pid, DWORD* Pids, int num)
{
	for (int i = 0; i < num; i++)
	{
		if (Pid == Pids[i])
		{
			return TRUE;
		}
	}
	return FALSE;
}

HANDLE DuplicateHandleEx(DWORD pid, HANDLE h, DWORD flags)
{
	HANDLE hHandle = NULL;

	HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
	if (hProc)
	{
		if (!DuplicateHandle(hProc,
			(HANDLE)h, GetCurrentProcess(),
			&hHandle, 0, FALSE, /*DUPLICATE_SAME_ACCESS*/flags))
		{
			hHandle = NULL;
		}
	}

	CloseHandle(hProc);
	return hHandle;
}

#pragma endregion



#pragma region 单独注入dll



BOOL InjectDll(char* dllPath)
{
	//获取微信Pid
	DWORD dwPid = ProcessNameFindPID(WECHAT_PROCESS_NAME);
	
	if (dwPid == 0) 
	{
		MessageBoxA(NULL, "没有找到微信进程 请先启动微信", "错误", 0);
		return FALSE;
	}

	//检测dll是否已经注入
	if (CheckIsInject(dwPid))
	{
		//打开进程
		HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
		if (hProcess == NULL)
		{
			MessageBoxA(NULL, "进程打开失败", "错误", 0);
			return FALSE;
		}
		//在微信进程中申请内存
		LPVOID pAddress = VirtualAllocEx(hProcess, NULL, MAX_PATH, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
		if (pAddress == NULL)
		{
			MessageBoxA(NULL, "内存分配失败", "错误", 0);
			return FALSE;
		}
		//写入dll路径到微信进程
		if (WriteProcessMemory(hProcess, pAddress, dllPath, MAX_PATH, NULL) == 0)
		{
			MessageBoxA(NULL, "路径写入失败", "错误", 0);
			return FALSE;
		}

		//获取LoadLibraryA函数地址
		FARPROC pLoadLibraryAddress = GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryA");
		if (pLoadLibraryAddress == NULL)
		{
			MessageBoxA(NULL, "获取LoadLibraryA函数地址失败", "错误", 0);
			return FALSE;
		}
		//远程线程注入dll
		HANDLE hRemoteThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pLoadLibraryAddress, pAddress, 0, NULL);
		if (hRemoteThread == NULL)
		{
			MessageBoxA(NULL, "远程线程注入失败", "错误", 0);
			return FALSE;
		}

		CloseHandle(hRemoteThread);
		CloseHandle(hProcess);
	}
	else
	{
		MessageBoxA(NULL, "dll已经注入，请勿重复注入", "提示", 0);
		return FALSE;
	}

	return TRUE;
}


DWORD ProcessNameFindPID(const char* ProcessName)
{
	PROCESSENTRY32 pe32 = { 0 };
	pe32.dwSize = sizeof(PROCESSENTRY32);
	HANDLE hProcess = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
	if (Process32First(hProcess, &pe32) == TRUE)
	{
		do
		{
			USES_CONVERSION;
			if (strcmp(ProcessName, W2A(pe32.szExeFile)) == 0)
			{
				return pe32.th32ProcessID;
			}
		} while (Process32Next(hProcess, &pe32));
	}
	return 0;
}

#pragma endregion




/// <summary>
/// 多开并注入微信
/// </summary>
/// <param name="dllPath">要注入的dll的绝对路径</param>
/// <returns></returns>
extern "C" _declspec(dllexport) int __stdcall WeChatMultiOpen(char* dllPath)
{
	PatchWeChat();
	int pid=OpenWeChat(dllPath, true);
	return pid;
}

/// <summary>
/// 只注入微信
/// </summary>
/// <param name="dllPath">要注入的dll的绝对路径</param>
/// <returns></returns>
extern "C" _declspec(dllexport) int __stdcall WeChatInject(char* dllPath)
{
	
	int pid = InjectDll(dllPath);
	return pid;
}


